/* Karenloar Utils */

// 插件
import id_card_region from "./plugins/karenloar/idCardRegion.js" // 身份证省市区对照表
import MD5 from "./plugins/karenloar/md5.js" // md5加密源码
import AES from "./plugins/karenloar/aes.js" // aes加密源码
import {
	pathToBase64
} from "./plugins/image-tools/image-tools.js" // uni图片转base64用
import html2canvas from "./plugins/html2canvas/html2canvas.min.js" // 导出pdf用
import JsPDF from "./plugins/jspdf/jspdf.es.min.js" // 导出pdf用
import drawQrcode from "./plugins/qrcode/weapp.qrcode.esm.js" // 绘制二维码用
import mobile_detect_import_result from "./plugins/mobile-detect/mobile-detect.min.js" // 浏览器环境判断设备类型用
import basicExportTem from "./plugins/karenloar/export-tem/basicExportTem.js" // 基础excel导出模板

export default (config = {}) => {
	let default_config = { // 默认配置列表
		autoMount: !0, // 是否自动挂载顶级对象:浏览器环境挂载window,uni-app环境挂载uni
		utilsName: "krUtils", // 工具类自动挂载到顶级对象中的名
		routeWatchTime: 10, // 路由变化监听间隔
		appendPrototype: !0, // 是否追加数据原型属性/方法
		appendPrototypeTable: { // 选择追加数据原型属性/方法
			string_to_hump: !0, // String转驼峰命名
			string_to_line: !0, // String转下划线命名
			object_for: !0, // 对象/数组强化for循环
			array_avgcut: !0, // 数组平均分割
		},
		exportTable: { // 导出html报表模板
			basicExportTem
		},
	}
	const loadConfig=(rec,def)=>{
		for(let key in def){
			if(rec[key]==undefined){
				rec[key]=def[key]
			}else{
				if(typeof rec[key]=="object"){
					loadConfig(rec[key],def[key])
				}
			}
		}
	}
	loadConfig(config,default_config)

	// 构建代理
	const buildProxy=(source,handle={})=>{
		let source_type=Object.prototype.toString.call(source).split(" ")[1].split("]")[0]
		if (!(source_type=="Object" || source_type=="Array")) {
			return source
		}
		let default_handle={
			get(self,param){
				let val_type=Object.prototype.toString.call(self[param]).split(" ")[1].split("]")[0]
				if(val_type=="Object" || val_type=="Array") {
					return buildProxy(self[param],handle)
				}else{
					return self[param]
				}
			},
			set(self,param,val){
				self[param]=val
				return !0
			}
		}
		loadConfig(handle,default_handle)
		let proxy = new Proxy(source,handle)
		return proxy
	}

	let core_params=buildProxy({ // 核心参数
		KARENLOAR:"Karenloar",
		control:{
			text:{
				project_id:"", // 项目id
			},
			obj:{
				route_watch:{}, // 路由
				debounce:{}, // 防抖
				throttle:{}, // 节流
			},
			list:{
				loop_sign:[], // 回溯循环标记列表
			},
		}
	})
	let source_fun={ // 本源方法
		ui:{ // ui类

		},
		routine:{ // 常规类
			buildProxy, // 构建代理
			checkWindowObj:(name,fun)=>{ // 检查是否存在window对象
				let check=!0
				try{
					window
				}catch{
					check=!1
				}
				if(!check){
					if(name) console.warn("karenloar-utils:未检测到window对象,无法使用"+name+"方法")
				}else{
					return fun()
				}
			},
			checkUniWindowObj:(name,fun)=>{ // 检查是否存在uni或window对象
				let check=!0
				try{
					uni
				}catch{
					check=!1
				}
				if(!check){
					check=!0
					try{
						window
					}catch {
						check=!1
					}
					if(!check){
						if(name) console.warn("karenloar-utils:未检测到uni或window对象,无法使用"+name+"方法")
					}else{
						return fun("window")
					}
				}else{
					return fun("uni")
				}
			},
			storageAction:(base,type,param,mode="l",val)=>{ // 缓存操作
				let kbex=(core_params.control.text.project_id?core_params.control.text.project_id+'-':''),
				kb="kr-storage-"+kbex,
				kbs="kr-storagesign-"+kbex
				switch(base){
					case "uni": // uni-app环境
						switch(type){
							case "get":
								return uni.getStorageSync(kb+param)
								break;
							case "set":
								uni.setStorageSync(kb+param,val)
								break;
							case "remove":
								uni.removeStorageSync(kb+param)
								break;
							case "get-all":
								let obj={}
								uni.getStorageInfoSync().keys.forEach(key=>{
									if(key.indexOf(kb)==0){
										let v_key=key.split(kb)[1]
										obj[v_key]=karenloarUtils.getItem(v_key,mode)
									}
								})
								return obj
								break;
							case "clear":
								for(let key in karenloarUtils.getAllItem(mode)){
									karenloarUtils.removeItem(key,mode)
								}
								break;
						}
						break;
					case "window": // 浏览器环境
						let kr_mode=(mode=="l"||mode=="local")?"localStorage":(mode=="s"||mode=="session")?"sessionStorage":"localStorage"
						switch(type){
							case "get":
								let data=window[kr_mode].getItem(kb+param)
								let sign=window[kr_mode].getItem(kbs+param)
								let final_data
								switch(sign){
									case "obj":
										final_data=JSON.parse(data)
										break;
									case "num":
										final_data=Number(data)
										break;
									case "undefined":
										break;
									case "str":
										final_data=data
										break;
									default:
										final_data=data
								}
								return final_data
								break;
							case "set":
								let save_type=""
								let save_val=val
								switch(typeof val){
									case "object":
									case "boolean":
										save_type="obj"
										save_val=JSON.stringify(val)
										break;
									case "number":
										save_type="num"
										break;
									case "string":
										// save_type = "str"
										break;
									case "undefined":
										save_type="undefined"
										break;
								}
								window[kr_mode].setItem(kb+param,save_val)
								window[kr_mode].setItem(kbs+param,save_type)
								break;
							case "remove":
								window[kr_mode].removeItem(kb+param)
								window[kr_mode].removeItem(kbs+param)
								break;
							case "get-all":
								let obj={}
								for(let key in window[kr_mode]) {
									if(key.indexOf(kb)==0){
										let v_key=key.split(kb)[1]
										obj[v_key]=karenloarUtils.getItem(v_key,mode)
									}
								}
								return obj
								break;
							case "clear":
								for(let key in karenloarUtils.getAllItem(mode)) {
									karenloarUtils.removeItem(key,mode)
								}
								break;
						}
						break;
				}
			},
			KrGlobalVariable: () => { // kr全局变量
				const KrGlobalVariable = function() {}
				let krGlobalVariable = new KrGlobalVariable()
				let root = buildProxy({})
				let default_gv_params_config = {
					set: (val) => {},
					enumerable: false,
					configurable: false,
				}
				Object.defineProperty(krGlobalVariable, "root", {
					get: function() {
						return root
					},
					...default_gv_params_config
				})
				Object.defineProperty(krGlobalVariable, "set", {
					get: function() {
						return function(param, value) {
							krGlobalVariable.root[param] = value
						}
					},
					...default_gv_params_config
				})
				Object.defineProperty(krGlobalVariable, "pset", {
					get: function() {
						return function(param, value) {
							krGlobalVariable.set(param, value)
							try {
								karenloarUtils.setItem(param, value)
							} catch (e) {
								console.warn("karenloar-utils:全局变量执行pset失败")
							}
						}
					},
					...default_gv_params_config
				})
				Object.defineProperty(krGlobalVariable, "get", {
					get: function() {
						return function(param) {
							if (krGlobalVariable.root[param]) {
								return krGlobalVariable.root[param]
							} else {
								try {
									return karenloarUtils.getItem(param)
								} catch (e) {
									return undefined
								}
							}
						}
					},
					...default_gv_params_config
				})
				Object.defineProperty(krGlobalVariable, "sget", {
					get: function() {
						return function(param) {
							setTimeout((toid) => {
								delete krGlobalVariable.root[param]
								try {
									karenloarUtils.removeItem(param)
								} catch (e) {}
								clearTimeout(toid)
							}, 0)
							if (krGlobalVariable.root[param]) {
								return krGlobalVariable.root[param]
							} else {
								try {
									return karenloarUtils.getItem(param)
								} catch (e) {
									return undefined
								}
							}
						}
					},
					...default_gv_params_config
				})
				return krGlobalVariable
			},
			convertColor: (color) => { // 颜色转换
				let base_num_list = []
				for(let i=0;i<4;i++){base_num_list.push(3,2)};base_num_list.push(3,3);for(let i=0;i<5;i++){base_num_list.push(2,3)}
				let hex_color_opacity_num_table=["0"],cn_sum=0
				for(let i=0;cn_sum<255;i++){cn_sum+=base_num_list[i%base_num_list.length];hex_color_opacity_num_table.push(cn_sum+"")}
				let hex_color_opacity_table = []
				karenloarUtils.for(hex_color_opacity_num_table,(k,i,v)=>{let r = parseInt(v,10).toString(16);r=(r.length==1?'0':'')+r;hex_color_opacity_table.push(r)})
				if (color.indexOf("rgb") != -1) { // rgb
					let separate1 = color.split("(")
					let separate2 = separate1[1].split(")")
					let separate3 = separate2[0].split(",")
					let r1 = parseInt(separate3[0], 10).toString(16)
					let r2 = parseInt(separate3[1], 10).toString(16)
					let r3 = parseInt(separate3[2], 10).toString(16)
					let result = "#" + (r1.length == 1 ? "0" + r1 : r1) + (r2.length == 1 ? "0" + r2 : r2) + (r3.length == 1 ? "0" + r3 : r3)
					if (separate3.length == 4) {
						let opacity_index
						if (separate3[3].indexOf("%") != -1) {
							opacity_index = Number(separate3[3].split("%")[0]).toFixed()
						} else {
							opacity_index = (Number(separate3[3]) * 100).toFixed()
						}
						result += hex_color_opacity_table[opacity_index]
					}
					return result.toLocaleLowerCase()
				} else if (color.indexOf("#") != -1) { // hex
					let base_str = "rgb"
					let color_val = color.split("#")[1]
					let c_color = color
					let pass = true
					switch (color_val.length) {
						case 3:
							c_color = "#" + color_val[0] + color_val[0] + color_val[1] + color_val[1] +
								color_val[2] + color_val[2]
							break;
						case 6:
							break;
						case 8:
							base_str += "a"
							break;
						default:
							pass = false
					}
					if (pass) {
						let separate1 = c_color.substring(1, 3)
						let separate2 = c_color.substring(3, 5)
						let separate3 = c_color.substring(5, 7)
						return base_str + "(" + parseInt(separate1, 16) + "," + parseInt(separate2, 16) + "," +
							parseInt(separate3, 16) + (base_str == "rgba" ? ("," + hex_color_opacity_table
								.findIndex(item => item.toLocaleLowerCase() == c_color.substring(7, 9)) +
								"%") : "") + ")"
					} else {
						console.warn("karenloar-utils:convertColor颜色转换:颜色参数错误")
					}
				} else {
					console.warn("karenloar-utils:convertColor颜色转换:颜色参数错误")
				}
			},
			trim: (val, type = "all") => { // 去空格
				val = val + ""
				if (type === "left" || type === "trim" || type === "all") {
					while ((val.indexOf(" ") == 0) && (val.length > 1)) {
						val = val.substring(1, val.length)
					}
				}
				if (type === "right" || type === "trim" || type === "all") {
					while ((val.lastIndexOf(" ") == val.length - 1) && (val.length > 1)) {
						val = val.substring(0, val.length - 1)
					}
				}
				if (val == " ") {
					val = ""
				} else if (type === "all") {
					while (val.indexOf(" ") !== -1) {
						val = val.substring(0, val.indexOf(" ")) + val.substring(val.indexOf(" ") + 1, val
							.length)
					}
				}
				return val
			},
			random: (type = "num", length = 1, dolt) => { // 随机数、随机字符、随机混合串
				if (type == "num") {
					let pd = 0
					let num = 0
					let decimal_pd = 0
					let decimal_num = 0
					do {
						num = Math.random()
						if (num < 0.1) {
							if (length == 1) {
								pd = 0
							} else {
								pd = 1
							}
						} else {
							pd = 0
						}
					} while (pd)
					for (let i = 1; i <= length; i++) {
						num *= 10
					}
					num = Number.parseInt(num)
					if (dolt) {
						do {
							decimal_num = Math.random()
							if (decimal_num < 0.1) {
								if (dolt > 1) {
									decimal_pd = 0
								} else {
									decimal_pd = 1
								}
							} else {
								decimal_pd = 0
							}
						} while (decimal_pd)
						decimal_num += ""
						decimal_num = decimal_num.substr(decimal_num.indexOf(".") + 1, dolt)
						if (decimal_num[decimal_num.length - 1] == 0) {
							let tail_patch = 0
							while (tail_patch < 1) {
								tail_patch = Number.parseInt(Math.random() * 10)
							}
							decimal_num = decimal_num.substr(0, decimal_num.length - 1) + tail_patch
						}
						decimal_num = "0." + decimal_num
						decimal_num = Number.parseFloat(decimal_num)
					}
					num += decimal_num
					return num
				} else if (type == "letter") {
					let num = 0
					let base
					let str = ""
					for (let i = 1; i <= length; i++) {
						num = 0
						while (num < 10) {
							num = Number.parseInt(Math.random() * 100)
						}
						num %= 26
						num += 1
						Number.parseInt(Math.random() * 100) % 2 == 0 ? base = 96 : base = 64
						if (dolt == "L" || dolt == "l" || dolt == "Lower" || dolt == "lower") base = 96
						if (dolt == "U" || dolt == "u" || dolt == "Upper" || dolt == "upper") base = 64
						str += String.fromCodePoint(base + num)
					}
					return str
				} else if (type == "blend") {
					let num = 0
					let small_letter = ""
					let small_letter_num = 0
					let large_letter = ""
					let large_letter_num = 0
					let pd = 0
					let str = ""
					for (let i = 1; i <= length; i++) {
						num = Math.random()
						num = Number.parseInt(num * 10) + ""
						small_letter_num = 0
						while (small_letter_num < 10) {
							small_letter_num = Number.parseInt(Math.random() * 100)
						}
						small_letter_num %= 26
						small_letter_num += 1
						small_letter = String.fromCodePoint(96 + small_letter_num)
						large_letter_num = 0
						while (large_letter_num < 10) {
							large_letter_num = Number.parseInt(Math.random() * 100)
						}
						large_letter_num %= 26
						large_letter_num += 1
						large_letter = String.fromCodePoint(64 + large_letter_num)
						pd = Number.parseInt(Math.random() * 1000)
						switch (pd % 3) {
							case 0:
								str += num
								break
							case 1:
								if (dolt == "U" || dolt == "u" || dolt == "Upper" || dolt == "upper") {
									str += large_letter
								} else {
									str += small_letter
								}
								break
							case 2:
								if (dolt == "L" || dolt == "l" || dolt == "Lower" || dolt == "lower") {
									str += small_letter
								} else {
									str += large_letter
								}
								break
						}
					}
					return str
				}
			},
			copy: (source) => { // 深拷贝
				switch (Object.prototype.toString.call(source).split(" ")[1].split("]")[0]) {
					case "RegExp":
						return new RegExp(source)
						break;
					case "Object":
						let obj = {}
						for (let key in source) {
							obj[key] = source_fun.routine.copy(source[key])
						}
						return obj
						break;
					case "Array":
						let array = []
						source.forEach((item, i) => {
							if (typeof item == "object") {
								array.push(source_fun.routine.copy(item))
							} else {
								array.push(item)
							}
						})
						return array
						break;
					case "Date":
						return new Date(source)
						break;
					default:
						return source
				}
			},
			getDomPosition: (dom_or_id, pd, fun) => { // 获取dom元素距页面顶部/左侧距离
				let dom
				switch (pd) {
					case "uni":
						switch (typeof dom_or_id) {
							case "object":
								dom = dom_or_id
								break;
							case "string":
								dom = uni.createSelectorQuery().in().select("#" + dom_or_id)
								break;
						}
						dom.boundingClientRect(data => {
							return fun(data)
						}).exec()
						break;
					case "window":
						switch (typeof dom_or_id) {
							case "object":
								dom = dom_or_id
								break;
							case "string":
								dom = document.getElementById(dom_or_id)
								break;
						}
						if (dom) {
							let top, left, width, height
							let parent = dom.offsetParent
							top = dom.offsetTop
							left = dom.offsetLeft
							width = dom.offsetWidth
							height = dom.offsetHeight
							while (parent != null) {
								top += parent.offsetTop
								left += parent.offsetLeft
								parent = parent.offsetParent
							}
							return {
								width,
								height,
								top,
								left,
								bottom: (document.documentElement.scrollHeight - top - height),
								right: (document.documentElement.scrollWidth - left - width)
							}
						} else {
							console.warn("karenloar-utils:getDomPosition获取dom元素坐标:未找到元素")
							return false
						}
						break;
				}
			},
			debounce: (obj, type) => { // 防抖
				if (type == "set-time") {
					clearTimeout(obj.timer)
					obj.timer = setTimeout(() => {
						obj.pd = false
						return obj.fun()
					}, obj.time)
				} else {
					switch (obj.mode) {
						case "n":
						case "normal":
							clearTimeout(obj.timer)
							obj.timer = setTimeout(() => {
								obj.pd = false
								return obj.fun()
							}, obj.time)
							break;
						case "i":
						case "immediate":
							if (obj.pd) {
								clearTimeout(obj.timer)
								obj.timer = setTimeout(() => {
									obj.pd = false
									return obj.fun()
								}, obj.time)
							} else {
								obj.pd = true
								return obj.fun()
							}
							break;
						case "i-once":
						case "immediate-once":
						case "immediateOnce":
							if (obj.once) {
								clearTimeout(obj.timer)
								obj.timer = setTimeout(() => {
									return obj.fun()
								}, obj.time)
							} else {
								obj.once = true
								return obj.fun()
							}
							break;
					}
				}
			},
			throttle: (obj, type) => { // 节流
				if (type == "set-time") {
					clearTimeout(obj.timer)
					obj.timer = setTimeout(() => {
						obj.pd = false
						clearTimeout(obj.timer)
					}, obj.time)
				} else {
					if (obj.pd) return
					obj.pd = true
					obj.timer = setTimeout(() => {
						obj.pd = false
						clearTimeout(obj.timer)
					}, obj.time)
					return obj.fun()
				}
			},
			clearDebounceOrThrottle: (type) => { // 清空防抖或节流对象
				let source = ""
				switch (type) {
					case "d":
					case "debounce":
						source = "debounce"
						break;
					case "t":
					case "throttle":
						source = "throttle"
						break;
				}
				if (source) {
					for (let key in core_params.control.obj[source]) {
						clearTimeout(core_params.control.obj[source][key].timer)
						delete core_params.control.obj[source][key]
					}
				}
			},
			KrDate: function(datetime) { // 日期时间
				let week_table = ["日", "一", "二", "三", "四", "五", "六"]
				this.y = datetime.getFullYear()
				this.yy = this.y % 100
				this.y_str = String(this.y)
				this.yy_str = (String(this.yy).length == 1 ? ("0" + this.yy) : "" + this.yy)
				this.M = datetime.getMonth() + 1
				this.M_str = (String(this.M).length == 1 ? ("0" + this.M) : "" + this.M)
				this.d = datetime.getDate()
				this.d_str = (String(this.d).length == 1 ? ("0" + this.d) : "" + this.d)
				this.H = datetime.getHours()
				this.H_str = (String(this.H).length == 1 ? ("0" + this.H) : "" + this.H)
				this.h = datetime.getHours() % 12
				this.h_str = (String(this.h).length == 1 ? ("0" + this.h) : "" + this.h)
				this.m = datetime.getMinutes()
				this.m_str = (String(this.m).length == 1 ? ("0" + this.m) : "" + this.m)
				this.s = datetime.getSeconds()
				this.s_str = (String(this.s).length == 1 ? ("0" + this.s) : "" + this.s)
				this.S = datetime.getMilliseconds()
				this.S_str = (String(this.S).length == 1 ? ("00" + this.S) : (String(this.S).length ==
						2) ?
					"0" + this.S : "" + this.S)
				this.a = (this.H < 12 ? "am" : "pm")
				this.w = week_table[datetime.getDay()]
				this.value = this.y_str + "-" + this.M_str + "-" + this.d_str + " " + this.H_str + ":" +
					this.m_str + ":" + this.s_str
				this.date = this.value.split(" ")[0]
				this.time = this.value.split(" ")[1]
				this.datetime = this.value
				this.stamp = datetime.getTime()
			},
			checkIDCard: (id_card_num) => { // 校验身份证是否正确
				let msg = "no error"
				id_card_num = id_card_num + ""
				id_card_num = id_card_num.toUpperCase()
				// 身份证号码为15位或者18位，15位时全为数字，18位前17位为数字，最后一位是校验位，可能为数字或字符X。
				if (!(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/.test(id_card_num))) {
					msg = "输入的身份证号长度不对,或者号码不符合规定!\n15位号码应全为数字,18位号码末位可以为数字或X"
					return {
						result: false,
						msg: msg
					}
				}
				// 校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
				// 下面分别分析出生日期和校验位
				let re
				const len = id_card_num.length
				if (len == 15) {
					re = new RegExp(/^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})$/)
					const arr_split = id_card_num.match(re)
					// 检查生日日期是否正确
					const dtm_birth = new Date("19" + arr_split[2] + "/" + arr_split[3] + "/" + arr_split[4])
					const b_good_day = (dtm_birth.getYear() == Number(arr_split[2])) && ((dtm_birth.getMonth() +
						1) == Number(arr_split[3])) && (dtm_birth.getDate() == Number(arr_split[4]))
					if (!b_good_day) {
						msg = "输入的身份证号里出生日期不对"
						return {
							result: false,
							msg: msg
						}
					} else {
						// 将15位身份证转成18位
						// 校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
						const arr_int = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
						const arr_ch = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]
						let n_temp = 0
						let i
						id_card_num = id_card_num.substr(0, 6) + "19" + id_card_num.substr(6, id_card_num
							.length - 6)
						for (i = 0; i < 17; i++) {
							n_temp += id_card_num.substr(i, 1) * arr_int[i]
						}
						id_card_num += arr_ch[n_temp % 11]
						return {
							result: true,
							id_card_18: id_card_num,
							msg: msg
						}
					}
				} else if (len == 18) {
					re = new RegExp(/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/)
					const arr_split = id_card_num.match(re)
					// 检查生日日期是否正确
					const dtm_birth = new Date(arr_split[2] + "/" + arr_split[3] + "/" + arr_split[4])
					const b_good_day = (dtm_birth.getFullYear() == Number(arr_split[2])) && ((dtm_birth
						.getMonth() + 1) == Number(arr_split[3])) && (dtm_birth.getDate() == Number(
						arr_split[4]))
					if (!b_good_day) {
						msg = "输入的身份证号出生日期不对"
						return {
							result: false,
							msg: msg
						}
					} else {
						// 检验18位身份证的校验码是否正确。
						// 校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
						// let val_num
						const arr_int = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
						const arr_ch = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]
						let n_temp = 0
						let i
						for (i = 0; i < 17; i++) {
							n_temp += id_card_num.substr(i, 1) * arr_int[i]
						}
						const val_num = arr_ch[n_temp % 11]
						if (val_num != id_card_num.substr(17, 1)) {
							msg = "18位身份证的校验码不正确"
							return {
								result: false,
								msg: msg
							}
						}
						return {
							result: true,
							msg: msg
						}
					}
				} else {
					msg = "输入的身份证号长度不对"
					return {
						result: false,
						msg: msg
					}
				}
			},
			getIDCardBirthday: (id_card_num) => { // 根据身份证获取生日
				let birthday = ""
				if (id_card_num != null && id_card_num != "") {
					if (id_card_num.length == 15) {
						birthday = "19" + id_card_num.slice(6, 12)
					} else if (id_card_num.length == 18) {
						birthday = id_card_num.slice(6, 14)
					}
					birthday = birthday.replace(/(.{4})(.{2})/, "$1-$2-")
				}
				return birthday
			},
			getIDCardSex: (id_card_num) => { // 根据身份证获取性别
				let sexStr = ""
				if (parseInt(id_card_num.slice(-2, -1)) % 2 == 1) {
					sexStr = "男"
				} else {
					sexStr = "女"
				}
				return sexStr
			},
			getIDCardRegion: (id_card_num) => { // 根据身份证获取省市区
				const num2 = id_card_num.substring(0, 2)
				const num4 = id_card_num.substring(0, 4)
				const num6 = id_card_num.substring(0, 6)
				let province = ""
				let city = ""
				let area = ""
				let province_tail = ""
				if (id_card_region[num2]) {
					if (id_card_region[num2].indexOf("自治区") != -1) {
						province = id_card_region[num2].substring(0, id_card_region[num2].indexOf("自治区"))
						province_tail = "自治区"
					} else {
						province = id_card_region[num2]
					}
					if (id_card_region[num4]) {
						if (id_card_region[num4] != province) {
							if (id_card_region[num4].indexOf(province) != -1) {
								city = id_card_region[num4].substr(id_card_region[num4].indexOf(province) +
									province.length)
							} else {
								city = id_card_region[num4]
							}
						} else {
							city = id_card_region[num4]
						}
						if (id_card_region[num6]) {
							let cacheStr = id_card_region[num6]
							if (cacheStr != province) {
								cacheStr = cacheStr.substr(cacheStr.indexOf(province) + province.length)
								if (id_card_region[num6].indexOf(city) != -1) {
									area = cacheStr.substr(cacheStr.indexOf(city) + city.length)
								} else {
									area = cacheStr
								}
							} else {
								area = cacheStr
							}
						}
					}
				}
				if (province_tail) province += province_tail
				return {
					province,
					city,
					area
				}
			},
			getAge: (birthday) => { // 获取年龄
				let card_year = Number(birthday.substring(0, 4))
				let card_month = Number(birthday.substring(5, 7))
				let card_day = Number(birthday.substring(8, 10))
				let cur_date = new Date()
				let cur_month = cur_date.getMonth() + 1
				let cur_day = cur_date.getDate()
				let age = cur_date.getFullYear() - card_year
				if (cur_month < card_month || (cur_month == card_month && cur_day < card_day)) {
					age--
				}
				return age
			},
			pathToBase64: (pd, url, backFun) => { // 根据图片地址获取base64
				if (url) {
					switch (pd) {
						case "uni":
							pathToBase64(url).then(res => {
									return backFun(res)
								})
								.catch(error => {
									console.error("karenloar-utils:pathToBase64图片转base64:" + error)
									return backFun(false)
								})
							break;
						case "window":
							window.URL = window.URL || window.webkitURL
							var xhr = new XMLHttpRequest()
							xhr.open("get", url, true)
							xhr.responseType = "blob"
							xhr.onload = function() {
								if (this.status == 200) {
									var blob = this.response
									let oFileReader = new FileReader()
									oFileReader.onloadend = function(e) {
										return backFun(e.target.result)
									}
									oFileReader.readAsDataURL(blob)
								} else {
									console.error("karenloar-utils:pathToBase64图片转base64:读取图片出错")
									return backFun(false)
								}
							}
							xhr.send()
							break;
					}
				} else {
					console.warn("karenloar-utils:pathToBase64图片转base64:请传入url")
				}
			},
			exportExcel: (data, name, style, type = "xlsx") => { // html转excel导出
				const ExportExcel = function() {
					let idTmr
					this.getExplorer = () => {
						let explorer = window.navigator.userAgent
						//ie
						if (explorer.indexOf("MSIE") >= 0) {
							return "ie"
						}
						//firefox
						else if (explorer.indexOf("Firefox") >= 0) {
							return "Firefox"
						}
						//Chrome
						else if (explorer.indexOf("Chrome") >= 0) {
							return "Chrome"
						}
						//Opera
						else if (explorer.indexOf("Opera") >= 0) {
							return "Opera"
						}
						//Safari
						else if (explorer.indexOf("Safari") >= 0) {
							return "Safari"
						}
					}
					// 判断浏览器是否为IE
					this.exportToExcel = (data, name, style) => {
						// 判断是否为IE
						if (this.getExplorer() == "ie") {
							this.tableToIE(data, name, style)
						} else {
							this.tableToNotIE(data, name, style)
						}
					}
					this.Cleanup = () => {
						window.clearInterval(idTmr)
					}
					// ie浏览器下执行
					this.tableToIE = (data, name) => {
						let curTbl = data
						let oXL = new ActiveXObject("Excel.Application")
						//创建AX对象excel
						let oWB = oXL.Workbooks.Add()
						//获取workbook对象
						let xlsheet = oWB.Worksheets(1)
						//激活当前sheet
						let sel = document.body.createTextRange()
						sel.moveToElementText(curTbl)
						//把表格中的内容移到TextRange中
						sel.select
						//全选TextRange中内容
						sel.execCommand("Copy")
						//复制TextRange中内容
						xlsheet.Paste()
						//粘贴到活动的EXCEL中
						oXL.Visible = true
						//设置excel可见属性
						try {
							let fname = oXL.Application.GetSaveAsFilename(
								"Excel.xlsx",
								"Excel Spreadsheets (*.xlsx), *.xlsx"
							)
						} catch (e) {
							print("Nested catch caught " + e)
						} finally {
							oWB.SaveAs(fname)
							oWB.Close((savechanges = false))
							//xls.visible = false;
							oXL.Quit()
							oXL = null
							// 结束excel进程，退出完成
							window.setInterval("Cleanup();", 1)
							idTmr = window.setInterval("Cleanup();", 1)
						}
					}
					// 非ie浏览器下执行
					this.tableToNotIE = (function() {
						// 编码要用utf-8不然默认gbk会出现中文乱码
						let uri = (type == "xlsx" ?
								"data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64," :
								"data:application/vnd.ms-excel;base64,"),
							template =
							'<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta charset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--><style>{style}</style></head><body>{table}</body></html>',
							base64 = function(s) {
								return window.btoa(unescape(encodeURIComponent(s)))
							},
							format = (s, c) => {
								return s.replace(/{(\w+)}/g, (m, p) => {
									return c[p]
								})
							}
						return (table, name, style) => {
							let ctx = {
								worksheet: name,
								table,
								style
							}
							//创建下载
							let link = document.createElement("a")
							link.setAttribute("href", uri + base64(format(template, ctx)))
							link.setAttribute("download", name + "." + type)
							link.click()
						}
					})()
				}
				return new ExportExcel().exportToExcel(data, name, style)
			},
			exportPDF: (name, id, win) => { // html转pdf导出
				var title = name
				let kr_document = win ? win.document : document
				html2canvas(kr_document.querySelector("#" + id), {
					allowTaint: true
				}).then(function(canvas) {
					let contentWidth = canvas.width
					let contentHeight = canvas.height
					let pageHeight = contentWidth / 592.28 * 841.89
					let leftHeight = contentHeight
					let position = 0
					let imgWidth = 595.28
					let imgHeight = 592.28 / contentWidth * contentHeight
					let pageData = canvas.toDataURL("image/jpeg", 1.0)
					let PDF = new JsPDF("", "pt", "a4")
					if (leftHeight < pageHeight) {
						PDF.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight)
					} else {
						while (leftHeight > 0) {
							PDF.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight)
							leftHeight -= pageHeight
							position -= 841.89
							if (leftHeight > 0) {
								PDF.addPage()
							}
						}
					}
					PDF.save(title + ".pdf")
					win ? win.close() : ""
				})
			},
			caesarEncrypt: (key, source) => { // 凯撒加密
				let constant_list = []
				for (let i = 0; i < 10; i++) {
					constant_list.push(i + "")
				}
				let key_index = 0
				let result = ""
				let other_source_type = false
				for (let source_index = 0; source_index < source.length; source_index++) {
					let offset = 0
					if (constant_list.indexOf(key[key_index]) != -1) {
						offset = Number(key[key_index])
					} else {
						let base = 64
						let key_code = key[key_index].charCodeAt()
						if (key_code >= 65 && key_code <= 90) {
							offset = key_code - base
						} else if (key_code >= 97 && key_code <= 122) {
							base = 96
							offset = -(key_code - base)
						} else {
							console.error("karenloar-utils:encrypt加密:传入key值超界")
							return false
						}
					}
					// if (constant_list.indexOf(source[source_index])!=-1) {
					//   offset = offset%10
					//   let offset_index = (constant_list.indexOf(source[source_index])+offset)%10
					//   if (offset_index<0) {
					//     offset_index = (10+offset_index)
					//   }
					//   result += constant_list[offset_index]
					// } else {
					let source_code = source[source_index].charCodeAt()
					let base = 64
					let offset_pd = 0
					let upper_limit = 9099993087
					// if (source_code>=65 && source_code<=90) {
					//   offset_pd = source_code - base
					// } else if (source_code>=97 && source_code<=122) {
					//   base = 96
					//   offset_pd = source_code - base
					// } else {
					other_source_type = true
					offset_pd = source_code
					// }
					let true_offset = (offset_pd + offset)
					if (!other_source_type) {
						if (offset_pd + offset <= 0) {
							true_offset = 26 + (offset_pd + offset)
						} else if (offset_pd + offset > 26) {
							true_offset = (offset_pd + offset) - 26
						}
					} else {
						if (offset_pd + offset <= 0) {
							true_offset = upper_limit + (offset_pd + offset)
						} else if (offset_pd + offset > upper_limit) {
							true_offset = (offset_pd + offset) - upper_limit
						}
					}
					// result += String.fromCharCode(base+true_offset)
					result += String.fromCharCode(true_offset)
					// }
					if (key_index == key.length - 1) {
						key_index = 0
					} else {
						key_index++
					}
				}
				return result
			},
			md5Encrypt: (key, source, md5_mode, md5_bits, md5_case, md5_code) => { // md5加密
				let result = ""
				let md5_mode_list = ["hex", "b64", "str", "hex_hmac", "b64_hmac", "str_hmac"]
				let mode = "hex"
				if (md5_mode && md5_mode_list.indexOf(md5_mode) != -1) {
					mode = md5_mode
				}
				let md5 = MD5({
					md5_bits,
					md5_case,
					md5_code
				})
				result = md5[mode](source, key)
				return result
			},
			aesEncrypt: (key, source, aes_mode, aes_iv, aes_padding) => { // aes加密
				let encrypted = ""
				let t_key = AES.enc.Utf8.parse(key)
				let t_iv = AES.enc.Utf8.parse(aes_iv)
				if(typeof(source) == "string") {
					let srcs = AES.enc.Utf8.parse(source)
					encrypted = AES.AES.encrypt(srcs, t_key, {
						iv: t_iv,
						mode: AES.mode[aes_mode],
						padding: AES.pad[aes_padding]
					})
				} else if(typeof(source) == "object") { //对象格式的转成json字符串
					let data = JSON.stringify(source)
					let srcs = AES.enc.Utf8.parse(data)
					encrypted = AES.AES.encrypt(srcs, t_key, {
						iv: t_iv,
						mode: AES.mode[aes_mode],
						padding: AES.pad[aes_padding]
					})
				}
				return encrypted.toString()
			},
			aesDecrypt: (key, source, aes_mode, aes_iv, aes_padding) => { // aes加密
				var t_key = AES.enc.Utf8.parse(key)
				var t_iv = AES.enc.Utf8.parse(aes_iv)
				var encryptedHexStr = AES.enc.Base64.parse(source)
				var srcs = AES.enc.Base64.stringify(encryptedHexStr)
				var decrypt = AES.AES.decrypt(srcs, t_key, {
					iv: t_iv,
					mode: AES.mode[aes_mode],
					padding: AES.pad[aes_padding]
				})
				var decryptedStr = decrypt.toString(AES.enc.Utf8)
				return decryptedStr.toString()
			},
			flat: (obj, sep = "__",fp = "") => { // obj数据扁平化
				let kr_obj = {}
				for(let key in obj){
					if(Object.prototype.toString.call(obj[key]).split(" ")[1].split("]")[0] == "Object"){
						let kr_fp = fp+key+sep
						kr_obj = {...kr_obj,...source_fun.routine.flat(obj[key],sep,kr_fp)}
					}else{
						kr_obj[fp+key] = obj[key]
					}
				}
				return kr_obj
			},
			layer: (obj, sep = "__") => { // obj数据扁平化恢复
				const buildLayer = (ko,kl,v,ki)=>{
					let i = ki || 0
					if((i+1)==kl.length){
						ko[kl[i]]=v
					}else{
						if(!ko[kl[i]]) ko[kl[i]]={}
						buildLayer(ko[kl[i]],kl,v,++i)	
					}
				}
				let kr_obj = {}
				for(let key in obj){
					let key_list = key.split(sep)
					if(key_list.length>1){
						buildLayer(kr_obj,key_list,obj[key])
					}else{
						kr_obj[key] = obj[key]
					}
				}
				return kr_obj
			}
		},
		data_prototype: { // 数据原型类
			appendPrototype: (s_str) => {
				let source
				let s_list = s_str.split("_")
				let str = s_str.substr(s_str.indexOf("_") + 1)
				switch (s_list[0]) {
					case "object":
						source = Object
						break;
					case "array":
						source = Array
						break;
					case "string":
						source = String
						break;
				}
				if (source) {
					Object.defineProperty(source.prototype, karenloarUtils.toHump(str), {
						get: function() {
							return source_fun.data_prototype.proto[s_list[0]][karenloarUtils.toHump(str)]
						},
						set: (val) => {},
						enumerable: !1,
						configurable: !0,
					})
				}
			},
			proto: {
				string: {
					toLine: function() {
						return karenloarUtils.toLine(this)
					},
					toHump: function() {
						return karenloarUtils.toHump(this)
					},
				},
				object: {
					for: function(loopFun) {
						return karenloarUtils.for(this,loopFun)
					},
				},
				array: {
					for: function(loopFun) {
						return karenloarUtils.for(this,loopFun)
					},
					avgcut: function(num) {
						return karenloarUtils.arrayAvgCut(this,num)
					},
				},
			},
		},
	}

	const KarenloarUtils = function() { // karenloar utils本体
		this.projectId = (id) => {
			if(id) core_params.control.text.project_id=id
			return core_params.control.text.project_id
		}
		this.setItem = (param, val, mode) => { // 写入缓存
			source_fun.routine.checkUniWindowObj("setItem", (pd) => {
				source_fun.routine.storageAction(pd, "set", param, mode, val)
			})
		}
		this.getItem = (param, mode) => { // 读取缓存
			return source_fun.routine.checkUniWindowObj("getItem", (pd) => {
				return source_fun.routine.storageAction(pd, "get", param, mode)
			})
		}
		this.removeItem = (param, mode) => { // 移除缓存
			source_fun.routine.checkUniWindowObj("removeItem", (pd) => {
				source_fun.routine.storageAction(pd, "remove", param, mode)
			})
		}
		this.getAllItem = (mode) => { // 读取全部利用本工具写入的缓存
			return source_fun.routine.checkUniWindowObj("getAllItem", (pd) => {
				return source_fun.routine.storageAction(pd, "get-all", undefined, mode)
			})
		}
		this.clearItem = (mode) => { // 清除全部利用本工具写入的缓存
			source_fun.routine.checkUniWindowObj("clearItem", (pd) => {
				source_fun.routine.storageAction(pd, "clear", undefined, mode)
			})
		}
		this.$gv = source_fun.routine.KrGlobalVariable() // 全局变量(推荐进行全局挂载操作)
		this.buildProxy = buildProxy
		this.convertColor = (color) => { // 颜色模式转换
			return source_fun.routine.convertColor(color)
		}
		this.toHump = (str) => { // 字符串格式转换-驼峰
			if (typeof str !== "string") {
				if (typeof str !== "undefined") console.warn("karenloar-utils:toHump字符串格式转换-驼峰:只能传入字符串")
				return
			}
			return str.replace(/\_(\w)/g, function(all, letter) {
				return letter.toUpperCase()
			})
		}
		this.toLine = (str) => { // 字符串格式转换-下划线
			if (typeof str !== "string") {
				if (typeof str !== "undefined") console.warn("karenloar-utils:toLine字符串格式转换-下划线:只能传入字符串")
				return
			}
			return str.replace(/([A-Z])/g, "_$1").toLowerCase()
		}
		this.for = (obj,loopFun) => { // 对象/数组强化for循环
			let i = 0,type = Object.prototype.toString.call(obj).split(" ")[1].split("]")[0]
			for(let key in obj){if(type == "Array") key = Number(key);let s = loopFun(key,i,obj[key],obj);i++;if(["c","continue","n","next"].indexOf(s)!=-1) continue;if(["b","break","e","end"].indexOf(s)!=-1) break;}
		}
		this.arrayAvgCut = (list,num) => { // 数组平均分割
			let r = [],c_l = this.copy(list)
			if(!num || num === 1) return c_l
			for(let i=0;i<c_l.length;) r.push(c_l.splice(i,i+num))
			return r
		}
		this.getSize = (source) => { // 获取数据大小/长度
			let result = 0
			switch (typeof source) {
				case "object":
					if(source!==null){
						if (source.length != undefined) result = source.length
						else result = Object.keys(source).length
					}
					break;
				case "number":
				case "string":
					result = (source + "").length
					break;
			}
			return result
		}
		this.trim = (val, type) => { // 去空格
			return source_fun.routine.trim(val, type)
		}
		this.random = (type, length, dolt) => { // 随机数、随机字符、随机混合串
			return source_fun.routine.random(type, length, dolt)
		}
		this.copy = (source) => { // 深拷贝
			return source_fun.routine.copy(source)
		}
		this.eval = (str, source) => { // 字符串转可执行语句
			return source_fun.routine.checkUniWindowObj("eval", (pd) => {
				switch (pd) {
					case "uni":
						console.warn("karenloar-utils:eval字符串转可执行语句:在uni环境下不可用")
						break;
					case "window":
						window.kr_source = source
						const kr_eval = "e" + "v" + "a" + "l"
						return window[kr_eval](str)
						break;
				}
			})
		}
		this.getDomPosition = (dom_or_id, fun) => { // 获取dom元素距页面顶部/左侧距离
			return source_fun.routine.checkUniWindowObj("getDomPosition", (pd) => {
				switch (pd) {
					case "uni":
						if (fun) {
							source_fun.routine.getDomPosition(dom_or_id, pd, (res) => {
								return fun(res)
							})
						} else {
							console.warn("karenloar-utils:getDomPosition获取dom坐标:缺少回调函数")
						}
						break;
					case "window":
						if (fun) {
							return fun(source_fun.routine.getDomPosition(dom_or_id, pd))
						} else {
							return source_fun.routine.getDomPosition(dom_or_id, pd)
						}
						break;
				}
			})
		}
		this.debounce = (id_or_fun, fun_or_time, time_or_mode, d_mode) => { // 防抖
			if (id_or_fun) {
				let id = "kr-debounce-default-id"
				let fun
				let time
				let mode
				const doDebounce = () => {
					if (!core_params.control.obj.debounce[id]) {
						core_params.control.obj.debounce[id] = {
							id,
							fun,
							time,
							mode,
							pd: false,
							once: false
						}
					} else {
						core_params.control.obj.debounce[id].fun = fun
					}
					return source_fun.routine.debounce(core_params.control.obj.debounce[id])
				}
				switch (typeof id_or_fun) {
					case "string": // id
						id = id_or_fun
						fun = fun_or_time
						time = time_or_mode || 0
						mode = d_mode || "normal"
						if (typeof fun == "function") {
							return doDebounce()
						} else {
							console.error("karenloar-utils:debounce防抖:传入fun类型错误")
						}
						break;
					case "function": // fun
						fun = id_or_fun
						time = fun_or_time || 0
						mode = time_or_mode || "normal"
						return doDebounce()
						break;
				}
			} else {
				console.warn("karenloar-utils:debounce防抖:请传入参数")
			}
		}
		this.setDebounceTime = (id_or_time, d_time) => { // 设置防抖时间
			if (id_or_time) {
				let id = "kr-debounce-default-id"
				let time
				if (d_time) {
					id = id_or_time
					time = d_time || 1
				} else {
					time = id_or_time || 1
				}
				core_params.control.obj.debounce[id].time = time
				source_fun.routine.debounce(core_params.control.obj.debounce[id], "set-time")
			} else {
				console.warn("karenloar-utils:setThrottleTime设置防抖时间:请传入参数")
			}
		}
		this.throttle = (id_or_fun, fun_or_time, t_time) => { // 节流
			if (id_or_fun) {
				let id = "kr-throttle-default-id"
				let fun
				let time
				const doThrottle = () => {
					if (!core_params.control.obj.throttle[id]) {
						core_params.control.obj.throttle[id] = {
							id,
							fun,
							time,
							pd: false
						}
					} else {
						core_params.control.obj.throttle[id].fun = fun
					}
					return source_fun.routine.throttle(core_params.control.obj.throttle[id])
				}
				switch (typeof id_or_fun) {
					case "string": // id
						id = id_or_fun
						fun = fun_or_time
						time = t_time || 0
						if (typeof fun == "function") {
							return doThrottle()
						} else {
							console.error("karenloar-utils:throttle节流:传入fun类型错误")
						}
						break;
					case "function": // fun
						fun = id_or_fun
						time = fun_or_time || 0
						return doThrottle()
						break;
				}
			} else {
				console.warn("karenloar-utils:throttle节流:请传入参数")
			}
		}
		this.setThrottleTime = (id_or_time, t_time) => { // 设置节流时间
			if (id_or_time) {
				let id = "kr-throttle-default-id"
				let time
				if (t_time) {
					id = id_or_time
					time = t_time || 1
				} else {
					time = id_or_time || 1
				}
				core_params.control.obj.throttle[id].time = time
				source_fun.routine.throttle(core_params.control.obj.throttle[id], "set-time")
			} else {
				console.warn("karenloar-utils:setThrottleTime设置节流时间:请传入参数")
			}
		}
		this.date = (dt) => { // 日期时间
			let t_dt = dt
			if(!t_dt) t_dt = new Date()
			let datetime
			try {
				datetime = new Date(t_dt)
			} catch {
				console.warn("karenloar-utils:date日期时间:传入参数必须是new Date()可转换的值")
				return
			}
			return new source_fun.routine.KrDate(datetime)
		}
		this.getAge = (birthday) => { // 获取年龄
			return source_fun.routine.getAge(birthday)
		}
		this.getIDCardInfo = (id_card_num) => { // 获取身份证信息
			let results = source_fun.routine.checkIDCard(id_card_num)
			let kr_id_card = id_card_num
			if (results.result) {
				if (results.id_card_18) kr_id_card = results.id_card_18
				results.birthday = source_fun.routine.getIDCardBirthday(kr_id_card)
				results.sex = source_fun.routine.getIDCardSex(kr_id_card)
				results.age = source_fun.routine.getAge(results.birthday)
				results = {
					...results,
					...source_fun.routine.getIDCardRegion(kr_id_card)
				}
			}
			return results
		}
		this.pathToBase64 = (url, fun) => { // 根据图片地址获取base64
			source_fun.routine.checkUniWindowObj("pathToBase64", (pd) => {
				source_fun.routine.pathToBase64(pd, url, (res) => {
					return fun(res)
				})
			})
		}
		this.qrcode = (params) => { // 生成二维码
			source_fun.routine.checkUniWindowObj("qrcode", (pd) => {
				const buildToDraw = (data)=>{
					if (data) {
						if(pd=="window") {
							data.width = data.offsetWidth
							data.height = data.offsetHeight
						}
						let obj = {
							canvasId: params.id,
							text: params.text || "感谢使用karenloar-utils",
							width: (pd=="uni"?data.width:data.offsetWidth),
							height: (pd=="uni"?data.height:data.offsetHeight),
							background: params.bg,
							foreground: params.fg,
							image: {}
						}
						if (!params.cx_lv) obj.typeNumber = -1
						else obj.typeNumber = (Number(params.cx_lv) + 3)
						if (!params.cr_lv) obj.correctLevel = 2
						else switch (Number(params.cr_lv)) {
							case 0:
								obj.correctLevel = 1
								break;
							case 1:
								obj.correctLevel = 0
								break;
							case 2:
								obj.correctLevel = 3
								break;
							case 3:
								obj.correctLevel = 2
								break;
						}
						if (params.img) {
							if (params.img.url) {
								obj.image.imageResource = params.img.url
								obj.image.dWidth = Number(params.img.width)
								obj.image.dHeight = Number(params.img.height)
								obj.image.dx = (obj.width / 2 - obj.image.dWidth / 2)
								obj.image.dy = (obj.height / 2 - obj.image.dHeight / 2)
							}
						}
						drawQrcode(obj,pd)
					} else {
						console.warn("karenloar-utils:qrcode生成二维码:未找到元素")
					}
				}
				switch (pd) {
					case "uni":
						let s = uni.createSelectorQuery().in()
						s.select("#" + params.id).boundingClientRect(data => {
							buildToDraw(data)
						}).exec()
						break;
					case "window":
						let dom = document.getElementById(params.id)
						buildToDraw(dom)
						break;
				}
			})
		}
		this.verificode = (params = {}, fun) => {
			source_fun.routine.checkUniWindowObj("verificode", (pd) => {
				const showError = (msg) => {
					console.warn("karenloar-utils:verificode生成验证码:" + msg)
				}
				const krGetSection = (max, min) => {
					let base = karenloarUtils.random('num', 0, 1)
					const limit = karenloarUtils.random('num', 1)
					if (limit == 1) {
						base = 0
					} else if (limit == 9) {
						base = 1
					}
					return base * (max - min) + min
				}
				const krGetRandomColor = (max, min, alpha) => {
					if (typeof max == 'string' || typeof max == 'number') {
						return 'rgba(' + krGetSection(max, min) + ',' + krGetSection(max, min) +
							',' + krGetSection(max, min) + ',' + (alpha || 1) + ')'
					} else if (typeof max == 'object') {
						return 'rgba(' + krGetSection((max.rMax || 255), (max.rMin || 0)) + ',' +
							krGetSection((max.gMax || 255), (max.gMin || 0)) + ',' + krGetSection((
								max.bMax || 255), (max.bMin || 0)) + ',' + (max.alpha || 1) + ')'
					}
				}
				if (params.id) {
					karenloarUtils.getDomPosition(params.id, (dom_info) => {
						let fontSize = dom_info.height / 1.35
						let pointR = fontSize / 20
						let default_params = {
							type:"text", // 类型:text:文本;compute:计算
							lineNum: 20, // 干扰线数量
							pointNum: 50, // 干扰点数量
							degOffset: 30, // 旋转角度偏移量
							sizeOffset: fontSize / 3.5, // 字符大小偏移量
							widthOffset: dom_info.width / 25, // 宽偏移量
							heightOffset: dom_info.height / 6, // 高偏移量
							length: 4, // 生成长度
							encrypt: "", // 加密模式
							neg:!0, // compute模式是否启用负数
						}
						if(params.type=="compute") {
							fontSize = dom_info.height / 2
							default_params.sizeOffset = fontSize / 3.5
							default_params.length = 2
						}
						let compare = {
							"+":"+",
							"-":"-",
							"*":"x",
							"/":"÷",
						}
						loadConfig(params, default_params)
						let topLv = 2.8
						let length
						if (params.text === undefined) {
							length = params.length
						} else {
							params.text += ""
							length = params.text.length
						}
						let spaceBase = dom_info.width / length
						if(params.type=="compute") spaceBase = dom_info.width / 3
						let space = spaceBase / 2
						let newHOffset
						let newWOffset
						let text = ""
						let ctx
						switch (pd) {
							case "uni":
								ctx = uni.createCanvasContext(params.id)
								break;
							case "window":
								let canvas = document.getElementById(params.id)
								if (canvas.getContext) {
									canvas.width = dom_info.width
									canvas.height = dom_info.height
									ctx = canvas.getContext("2d")
								} else {
									showError("请确认id指向为canvas元素,或浏览器支持canvas")
									return
								}
								break;
						}
						ctx.fillStyle = krGetRandomColor(255, 200)
						ctx.fillRect(0, 0, dom_info.width, dom_info.height)
						ctx.textAlign = 'center'
						ctx.save()
						for (let i = 1; i <= params.pointNum; i++) {
							ctx.beginPath()
							ctx.arc(krGetSection(0, dom_info.width), krGetSection(0, dom_info
								.height), pointR, 0, 2 * Math.PI)
							ctx.fillStyle = krGetRandomColor(255, 0)
							ctx.fill()
							ctx.closePath()
						}
						for (let i = 1; i <= params.lineNum; i++) {
							ctx.beginPath()
							ctx.moveTo(krGetSection(0, dom_info.width), krGetSection(0,
								dom_info.height))
							ctx.lineTo(krGetSection(0, dom_info.width), krGetSection(0,
								dom_info.height))
							ctx.strokeStyle = krGetRandomColor(255, 0)
							ctx.stroke()
							ctx.closePath()
						}
						const drawFun = (newText,i)=>{
							newHOffset = krGetSection(params.heightOffset, -params.heightOffset)
							newWOffset = krGetSection(params.widthOffset, -params.widthOffset)
							ctx.translate(space + spaceBase * i + newWOffset, dom_info.height /
								2 +
								fontSize / topLv + newHOffset)
							ctx.rotate(Math.PI / 180 * (krGetSection(params.degOffset, -params
								.degOffset)))
							ctx.fillStyle = krGetRandomColor(120, 0)
							ctx.font = (parseInt(fontSize + krGetSection(params.sizeOffset, -
								params
								.sizeOffset)) + 'px sans-serif')
							ctx.fillText(newText, 0, 0)
							ctx.restore()
							ctx.save()
						}
						if(params.type == "text"){
							for (let i = 0; i < length; i++) {
								let newText
								if (params.text === undefined) {
									newText = karenloarUtils.random("blend", 1)
								} else {
									newText = params.text[i]
								}
								text += newText
								drawFun(newText,i)
							}
						}else if(params.type == "compute"){
							let c_obj = {},c_ncl = ["lsize","rsize"],c_ncnl = ["lneg","rneg"]
							if(params.text){
								let reg = new RegExp(/^-?[0-9]+[+-/*]-?[0-9]+$/)
								if(reg.test(params.text)){
									karenloarUtils.for(compare,(k,i,v)=>{
										let sp_l=params.text.split(k)
										if(sp_l.length==2){
											c_obj.lnumtext = sp_l[0];c_obj.rnumtext = sp_l[1];
											c_obj.lnum = Number(sp_l[0]);c_obj.rnum = Number(sp_l[1]);
											c_obj.op = k;c_obj.optext = v;return "b";
										}
									})
									switch(c_obj.op){
										case "+":text=(c_obj.lnum+c_obj.rnum);break;
										case "-":text=(c_obj.lnum-c_obj.rnum);break;
										case "*":text=(c_obj.lnum*c_obj.rnum);break;
										case "/":text=(c_obj.lnum/c_obj.rnum);break;
									}
								}else{showError("text不合规");return}
							}else{
								const codeBuild= ()=>{
									for(let c_p of c_ncl) {while((!c_obj[c_p] || c_obj[c_p]>length)){c_obj[c_p] = karenloarUtils.random("num", 1)}}
									c_obj.lnum = karenloarUtils.random("num", c_obj.lsize)
									c_obj.rnum = karenloarUtils.random("num", c_obj.rsize)
									c_obj.lnumtext = c_obj.lnum+"";c_obj.rnumtext = c_obj.rnum+""
									if(params.neg) {
										for(let c_p of c_ncnl) {while((!c_obj[c_p] || c_obj[c_p]>2)){c_obj[c_p] = karenloarUtils.random("num", 1)}}
										if(c_obj.lneg === 1) {c_obj.lnum*=-1;c_obj.lnumtext = ("("+c_obj.lnum+")")}
										if(c_obj.rneg === 1) {c_obj.rnum*=-1;c_obj.rnumtext = ("("+c_obj.rnum+")")}
									}
								}
								while(!c_obj.rnum){codeBuild()}
								while((!c_obj.op || c_obj.op>4)){c_obj.op = karenloarUtils.random("num", 1)}
								karenloarUtils.for(compare,(k,i,v)=>{if((i+1)==c_obj.op) {c_obj.op = k;c_obj.optext = v;}})
								switch(c_obj.op){
									case "+":text=(c_obj.lnum+c_obj.rnum);break;
									case "-":text=(c_obj.lnum-c_obj.rnum);break;
									case "*":text=(c_obj.lnum*c_obj.rnum);break;
									case "/":text=(c_obj.lnum/c_obj.rnum);break;
								}
								while((text+"").indexOf(".")!=-1){
									do{codeBuild()}while(!c_obj.rnum)
									text=(c_obj.lnum/c_obj.rnum)
								}
							}
							let r_l = [c_obj.lnumtext,c_obj.optext,c_obj.rnumtext]
							karenloarUtils.for(r_l,(k,i,v)=>{drawFun(v,i)})
						}
						if (pd == "uni") ctx.draw()
						
						let en_mod = params.encrypt
						if(typeof en_mod == "string") en_mod = en_mod.toLocaleLowerCase()
						switch (en_mod) {
							case "caesar":
							case "md5":
							case "aes":
								return fun(karenloarUtils.encrypt({
									source: text+"",
									type: params.encrypt,
									key:params.key,
									mode:params.mode,
									bits:params.bits,
									case:params.case,
									code:params.code,
									iv:params.iv,
									padding:params.padding,
								}))
								break;
							default:
								return fun(text)
						}
					})
				} else {
					showError("请传入id")
				}
			})
		}
		this.exportTable = ({
			tem,
			data,
			name,
			pdf_config = {}
		}, fun_or_type) => { // 导出html报表
			source_fun.routine.checkUniWindowObj("exportTable", (pd) => {
				switch (pd) {
					case "uni":
						console.warn("karenloar-utils:exportTable导出html报表:在uni环境下不可用")
						break;
					case "window":
						let default_pdf_config = {
							padding: "45px",
							zoom: 1,
						}
						loadConfig(pdf_config, default_pdf_config)
						const krBackFun = (table_name, table, style, fun_or_type) => {
							if (fun_or_type) {
								let html_table =
									"<div id='kr-export-pdf'>" + table + "</div>"
								let new_win_html_table =
									"<div id='kr-export-pdf' style='width:max-content;padding:" +
									pdf_config
									.padding + ";zoom:" + pdf_config.zoom +
									";' class='kr-center-r'>" +
									table + "</div><style>" +
									".kr-center-r{position:relative;left:50%;transform: translate(-50%, 0);}" +
									style +
									"</style>"
								if (typeof fun_or_type == "function") {
									return fun_or_type({
										htable: html_table,
										epExcel: (type) => {
											let t_type = "xlsx"
											if (type == "xls") t_type = type
											source_fun.routine.exportExcel(table,
												table_name,
												style, t_type)
										},
										epPDF: () => {
											let newWin = window.open()
											newWin.document.write(new_win_html_table)
											newWin.document.title = "打印pdf"
											setTimeout(() => {
												source_fun.routine.exportPDF(
													table_name,
													"kr-export-pdf", newWin)
											}, 0)
										}
									})
								} else if (typeof fun_or_type == "string") {
									if (fun_or_type == "excel" || fun_or_type == "Excel") {
										source_fun.routine.exportExcel(table, table_name, style)
									} else if (fun_or_type == "excel-xls" || fun_or_type ==
										"Excel-xls") {
										source_fun.routine.exportExcel(table, table_name, style,
											"xls")
									} else if (fun_or_type == "pdf" || fun_or_type == "PDF") {
										let newWin = window.open()
										newWin.document.write(new_win_html_table)
										newWin.document.title = "打印pdf"
										setTimeout(() => {
											source_fun.routine.exportPDF(table_name,
												"kr-export-pdf", newWin)
										}, 0)
									} else {
										console.warn(
											"karenloar-utils:exportTable导出报表:fun_or_type参数非有效值")
									}
								} else {
									console.warn(
										"karenloar-utils:exportTable导出报表:fun_or_type参数类型错误")
								}
							} else {
								console.warn("karenloar-utils:exportTable导出报表:缺少fun_or_type参数")
							}
						}
						const krLoad = (tem, table_data, table_name, fun_or_type) => {
							tem(table_data, table_name, (table, style) => {
								krBackFun(table_name, table, style, fun_or_type)
							})
						}
						const krExport = (table_tem, ...params) => {
							let ep_tem
							if (typeof table_tem == "function") {
								ep_tem = table_tem
							} else if (typeof table_tem == "string") {
								ep_tem = config.exportTable[table_tem]
							} else {
								console.warn("karenloar-utils:exportTable导出报表:tem参数类型错误")
								return
							}
							krLoad(ep_tem, ...params)
						}
						return krExport(tem, data, name, fun_or_type)
						break;
				}
			})
		}
		this.encrypt = (params) => { // 加密
			function getKey() {
				let pre_key = ""
				for (let i = 0; i < core_params.KARENLOAR.length; i++) {
					if ([0, 2, 5, 8].indexOf(i) != -1) {
						pre_key += core_params.KARENLOAR[i].toUpperCase()
					} else {
						pre_key += core_params.KARENLOAR[i]
					}
				}
				return pre_key
			}
			if (params && typeof params == "object") {
				if (params.source != undefined) {
					let type = params.type || "MD5"
					let key = params.key || getKey()
					let source = params.source
					let md5_mode = params.mode || "hex"
					let md5_bits = params.bits || 32
					let md5_case = params.case || "l"
					let md5_code = params.code || "ASCII"
					let aes_iv = params.iv || getKey().substring(0,8)
					let aes_mode = params.mode || "CBC"
					let aes_padding = params.padding || "Pkcs7"
					type = type.toLowerCase()
					switch (type) {
						case "caesar":
							return source_fun.routine.caesarEncrypt(key, source + "")
							break;
						case "md5":
							return source_fun.routine.md5Encrypt(key, source, md5_mode, md5_bits, md5_case,
								md5_code)
							break;
						case "aes":
							if(key == getKey()) key = key.substring(0,8)
							return source_fun.routine.aesEncrypt(key, source, aes_mode, aes_iv, aes_padding)
							break;
					}
				} else {
					console.warn("karenloar-utils:encrypt加密:需要传递加密源source")
				}
			} else {
				console.warn("karenloar-utils:encrypt加密:需要传Object类型参数")
			}
		}
		this.decrypt = (params) => { // 解密
			function getKey() {
				let pre_key = ""
				for (let i = 0; i < core_params.KARENLOAR.length; i++) {
					if ([0, 2, 5, 8].indexOf(i) != -1) {
						pre_key += core_params.KARENLOAR[i].toUpperCase()
					} else {
						pre_key += core_params.KARENLOAR[i]
					}
				}
				return pre_key
			}
			if (params && typeof params == "object") {
				if (params.source) {
					let type = params.type || "MD5"
					let key = params.key || getKey()
					let source = params.source
					let aes_iv = params.iv || getKey().substring(0,8)
					let aes_mode = params.mode || "CBC"
					let aes_padding = params.padding || "Pkcs7"
					let constant_list = []
					for (let i = 0; i < 10; i++) {
						constant_list.push(i + "")
					}
					let key_index = 0
					type = type.toLowerCase()
					switch (type) {
						case "caesar":
							let caesar_re_key = ""
							for (let key_index = 0; key_index < key.length; key_index++) {
								if (constant_list.indexOf(key[key_index]) != -1) {
									if (key[key_index] == "0") {
										caesar_re_key += (10 - Number(key[key_index])) + ""
									} else {
										caesar_re_key += String.fromCharCode(96 + Number(key[key_index]))
									}
								} else {
									let key_code = key[key_index].charCodeAt()
									if (key_code >= 65 && key_code <= 90) {
										caesar_re_key += key[key_index].toLowerCase()
									} else if (key_code >= 97 && key_code <= 122) {
										caesar_re_key += key[key_index].toUpperCase()
									} else {
										console.error("karenloar-utils:decrypt解密:传入key值超界")
										return false
									}
								}
							}
							return this.encrypt({
								source: source,
								key: caesar_re_key,
								type: "Caesar"
							})
							break;
						case "md5":
							console.warn("karenloar-utils:decrypt解密:MD5加密数据无法解密")
							return null
							break;
						case "aes":
							if(key == getKey()) key = key.substring(0,8)
							return source_fun.routine.aesDecrypt(key, source, aes_mode, aes_iv, aes_padding)
							break;
					}
				} else {
					console.warn("karenloar-utils:decrypt解密:需要传递加密源source")
				}
			} else {
				console.warn("karenloar-utils:decrypt解密:需要传Object类型参数")
			}
		}
		this.merge=loadConfig
		this.flat=(obj,sep)=>{ // obj数据扁平化
			if(Object.prototype.toString.call(obj).split(" ")[1].split("]")[0]=="Object"){
				return source_fun.routine.flat(obj,sep)
			}else{
				console.warn("karenloar-utils:flat对象扁平化:参数obj必须为Object")
			}
		}
		this.layer=(obj,sep)=>{ // obj数据扁平化恢复
			if(Object.prototype.toString.call(obj).split(" ")[1].split("]")[0]=="Object"){
				return source_fun.routine.layer(obj,sep)
			}else{
				console.warn("karenloar-utils:flat对象扁平化恢复:参数obj必须为Object")
			}
		}
		this.sublog=(params)=>{ // 参数打印(辅助)
			if(params){
				console.log("——————————sublog——————————")
				if(typeof params=="object") karenloarUtils.for(params,(k,i,v)=>{console.log(k+":"+(typeof v=="string"?(`"${v}"`):v))})
				else console.log(params)
				console.log("——————————————————————————")
			}
		}
		this.getAdaptZoom=(width)=>{ // 获取适应变焦
			return source_fun.routine.checkUniWindowObj("getAdaptZoom", (pd) => {
				switch (pd) {
					case "uni":{
						return wx.getSystemInfoSync().windowWidth/parseFloat(width)
					}break;
					case "window":{
						return document.documentElement.clientWidth/parseFloat(width)
					}break;
				}
			})
		}
		this.loopSign=(fun)=>{ // 回溯循环
			let pd = !0,sign_text="karenloarUtils_goto_loop_"+karenloarUtils.random('blend',8)
			if(core_params.control.list.loop_sign.indexOf(sign_text)==-1) core_params.control.list.loop_sign.push(sign_text)
			while(pd){
				let cf=()=>{throw sign_text}
				try{
					fun(cf,sign_text)
				}catch(e){
					if(e==sign_text) continue
					else throw e
				}
				break
			}
		}
		this.loopSign.getSignList=()=>{return karenloarUtils.copy(core_params.control.list.loop_sign)}
		this.deviceType=()=>{ // 获取设备类型
			return source_fun.routine.checkUniWindowObj("deviceType", (pd) => {
				switch (pd) {
					case "uni":{
						let dt=uni.getSystemInfoSync().deviceType
						if(dt=="pad") dt="tablet"
						return dt
					}break;
					case "window":{
						if(mobile_detect_import_result){
							let md=new window.MobileDetect(navigator.userAgent)
							if(md.phone()) return "phone";
							if(md.tablet()) return "tablet";
							return "pc"
						}else{
							console.warn("karenloar-utils:deviceType获取设备类型:核心载入失败")
							return !1
						}
					}break;
				}
			})
		}
	}

	let karenloarUtils = new KarenloarUtils()
	// 去除当前环境不适配的方法
	let remove_list = ["eval","exportTable"]
	source_fun.routine.checkUniWindowObj("",(pd)=>{
		switch (pd) {
			case "uni":
				remove_list.forEach(key=>{delete karenloarUtils[key]})
				break;
		}
	})
	// 是否自动挂载顶级对象,uni-app兼容
	if (config.autoMount === true) {
		source_fun.routine.checkUniWindowObj("",(pd)=>{
			switch (pd) {
				case "uni":
					uni[config.utilsName] = karenloarUtils
					break;
				case "window":
					window[config.utilsName] = karenloarUtils
					break;
			}
		})
	}
	// 路由监听
	source_fun.routine.checkUniWindowObj("",(pd)=>{
		switch (pd) {
			case "uni":
				clearInterval(uni["kr-route-watch-timer"])
				uni["kr-route-watch-timer"] = setInterval(() => {
					let route = getCurrentPages()
					if (route.length) {
						if (core_params.control.obj.route_watch.cur_path != route[route.length - 1]
							.route) {
							core_params.control.obj.route_watch.old_path = core_params.control.obj
								.route_watch.cur_path
							core_params.control.obj.route_watch.cur_path = route[route.length - 1].route
							source_fun.routine.clearDebounceOrThrottle("d") // 清除防抖
							source_fun.routine.clearDebounceOrThrottle("t") // 清除节流
						}
					}
				}, config.routeWatchTime)
				break;
			case "window":
				clearInterval(window["kr-route-watch-timer"])
				break;
		}
	})
	// 追加原型属性/方法
	if (config.appendPrototype) {
		let load_all = true
		if (config.appendPrototype == "choose") load_all = false
		for (let key in config.appendPrototypeTable) {
			if ((!load_all) && (!config.appendPrototypeTable[key])) {} else {
				source_fun.data_prototype.appendPrototype(key)
			}
		}
	}

	return karenloarUtils
}